package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.jsola.admin.entity.GregorianCalendarDO;
import org.jsola.admin.query.GregorianCalendarQuery;
import org.jsola.common.DateKit;
import org.jsola.exception.ParamException;
import org.jsola.hr.constant.DateType;
import org.jsola.hr.constant.PointTimeType;
import org.jsola.hr.constant.PunchType;
import org.jsola.hr.constant.RuleType;
import org.jsola.hr.dto.AttendAdvancedDTO;
import org.jsola.hr.dto.PunchCommonDTO;
import org.jsola.hr.dto.attendrule.AttendRulePunchTimeAddDTO;
import org.jsola.hr.dto.attendschedule.AttendPeriodDTO;
import org.jsola.hr.dto.attendschedule.AttendShiftSaveDTO;
import org.jsola.hr.entity.AttendPunchRecordDO;
import org.jsola.hr.entity.AttendPunchTimeDO;
import org.jsola.hr.provider.IGregorianCalendarProviderService;
import org.jsola.hr.query.AttendPunchRecordQuery;
import org.jsola.hr.query.AttendPunchTimeQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.AttendNeedPunchVO;
import org.jsola.hr.vo.AttendPointTimeVO;
import org.jsola.hr.vo.PunchCommonVO;
import org.jsola.hr.vo.WorkStatusCommonVO;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author wxz
 */
@Slf4j
@Service("hrAttendPunchCommonServiceImpl")
public class AttendPunchCommonServiceImpl implements IAttendPunchCommonService {

    @Autowired
    private IAttendWhitelistService attendWhitelistService;
    @Autowired
    private IAttendCalendarService attendCalendarService;
    @Autowired
    private IAttendPunchTimeService attendPunchTimeService;
    @Autowired
    private IAttendShiftService attendShiftService;
    @Autowired
    private IAttendPunchRecordService attendPunchRecordService;
    @Autowired
    private IAttendPunchCalibrationService attendPunchCalibrationService;
    @Autowired
    private IGregorianCalendarProviderService gregorianCalendarProviderService;

    @Override
    public PunchCommonVO punchCommon(PunchCommonDTO punchCommonDTO, TokenUser tokenUser) {

        // 是否在白名单中
        boolean isWhitelist = attendWhitelistService.isWhitelist(punchCommonDTO.getAttendPlanId(), punchCommonDTO.getEmpInfoId(), tokenUser.getSiteId());
        if (isWhitelist) {
            log.info("在白名单中");
            return new PunchCommonVO().buildWhitelist();
        }
        // 当前日期是否需要打卡
        AttendNeedPunchVO attendNeedPunchVO = attendCalendarService.isNeedPunchOfDaily(punchCommonDTO.getAttendPlanId(), punchCommonDTO.getAttendDateStr(), tokenUser.getSiteId());

        // 是特殊日期，特殊日期需要打卡
        boolean isSpecialPunch = false;
        // 是特殊日期，并且特殊日期不需要打卡
        if (attendNeedPunchVO.getIsSpecialDate()) {
            if (attendNeedPunchVO.getIsNeedPunchOfDaily()) {
                log.info("是特殊日期，并且特殊日期需要打卡");
                isSpecialPunch = true;
            } else {
                log.info("是特殊日期，并且特殊日期不需要打卡");
                return new PunchCommonVO().buildSpecial();
            }
        }
        PunchCommonVO punchCommonVO = new PunchCommonVO();

        // 考勤时段
        List<AttendPeriodDTO> periodList = null;
        // 考勤周
        List<String> workWeekList = null;
        // 考勤高级设置
        AttendAdvancedDTO advanced = null;
        // 今日是否需要考勤;true-是,false-否
        boolean isTodayAttend = true;
        // 当前时间
        Date now = DateKit.getToday();
        // 第一个时间点
        String firstOnWork = "";
        // 最后一个时间点
        String lastOffWork = "";

        AttendRulePunchTimeAddDTO attendRulePunchTime = null;
        // 查询公历表，日期类型 1休息日,2-工作日,3公假日
        // 查询admin的公历记录，先查询租户的，租户的没有就查询默认的
        GregorianCalendarQuery gregorianCalendarQuery = new GregorianCalendarQuery();
        String[] data = punchCommonDTO.getAttendDateStr().split("-");
        gregorianCalendarQuery.setYear(Integer.parseInt(data[0]));
        gregorianCalendarQuery.setMonth(Integer.parseInt(data[1]));
        gregorianCalendarQuery.setDay(Integer.parseInt(data[2]));
        List<GregorianCalendarDO> gregorianCalendarDOList = gregorianCalendarProviderService
                .selectClendar(gregorianCalendarQuery, tokenUser.getSiteId());

        // 考勤规则类型- 固定时间上下班
        if (punchCommonDTO.getPlanType().equals(RuleType.FIXED_TIME.getValue()) && !isSpecialPunch) {
            log.info("勤规则类型- 固定时间上下班");
            // 固定上下班的打卡时间
            attendRulePunchTime = attendPunchTimeService.puncheTimeForFixed(punchCommonDTO.getAttendPlanId(), punchCommonDTO.getAttendDateStr(), tokenUser);

            if (attendRulePunchTime == null) {
                isTodayAttend = false;
                log.info("今日不在考勤时间范围内，无需打卡");

                if (!punchCommonDTO.getIsSynchLegalHoliday()) {
                    // 不同步节假日  直接返回不需要打卡
                    return new PunchCommonVO().buildNotNeedAttend();
                } else {
                    // 同步节假日
                    ParamException.isFalse(CollectionUtils.isEmpty(gregorianCalendarDOList),
                            "同步中国节假日必须先同步日历信息!");
                    GregorianCalendarDO gregorianCalendarDO = gregorianCalendarDOList.get(0);
                    // 本日不是公历同步的信息 直接返回不需要打卡
                    if (!gregorianCalendarDO.getIsCalendar()) {
                        return new PunchCommonVO().buildNotNeedAttend();
                    }

                }
            } else {
                periodList = attendRulePunchTime.getPeriodList();
                workWeekList = attendRulePunchTime.getWorkWeekList();
                advanced = attendRulePunchTime.to(AttendAdvancedDTO.class);
                advanced.setAttendPunchTimeId(attendRulePunchTime.getAttendPunchTimeId());
                firstOnWork = periodList.get(0).getStartPeriod();
                lastOffWork = periodList.get(periodList.size() - 1).getEndPeriod();

            }
        }
        // 按班次上下班
        else if (punchCommonDTO.getPlanType().equals(RuleType.BY_SHIFT.getValue()) && !isSpecialPunch) {
            log.info("勤规则类型- 按班次上下班");
            AttendShiftSaveDTO attendShift = attendShiftService.getAttendShift(punchCommonDTO.getAttendPlanId(), punchCommonDTO.getEmpInfoId(), punchCommonDTO.getAttendDateStr(), tokenUser);
            if (attendShift == null) {
                isTodayAttend = false;
                log.info("今日不在考勤时间范围内，无需打卡");
                return new PunchCommonVO().buildNotNeedAttend();
            } else {
                periodList = attendShift.getAttendPeriodList();
                advanced = attendShift.to(AttendAdvancedDTO.class);
                lastOffWork = periodList.get(periodList.size() - 1).getEndPeriod();
            }
        }
        // 自由上下班
        else if (punchCommonDTO.getPlanType().equals(RuleType.FREE_WORK.getValue()) && !isSpecialPunch) {
            log.info("勤规则类型- 自由上下班");
            // 查询打卡工作周
            workWeekList = attendPunchTimeService.getFreeWork(punchCommonDTO.getAttendPlanId(), punchCommonDTO.getAttendDateStr(), tokenUser);
            if (workWeekList == null) {
                isTodayAttend = false;
                log.info("今日不在考勤时间范围内，无需打卡");
                return new PunchCommonVO().buildNotNeedAttend();
            }
        }

        // 是否同步中国节假日 ; 1-是,0-否
        if (punchCommonDTO.getIsSynchLegalHoliday() && !isSpecialPunch) {
            log.info("同步中国节假日");
            if (gregorianCalendarDOList.size() > 0 && gregorianCalendarDOList.get(0).getIsCalendar()) {
                GregorianCalendarDO gregorianCalendarDO = gregorianCalendarDOList.get(0);
                // todo 判断是否是工作日
                if (DateType.WORK_DAY.getValue().equals(gregorianCalendarDO.getDateType())) {
                    log.info("同步中国节假日---> 需要打卡");
                    isTodayAttend = true;
                    // 根据考勤规则ID查询考勤打卡时间(hr_attend_punch_time)
                    AttendPunchTimeQuery attendPunchTimeQuery = new AttendPunchTimeQuery();
                    attendPunchTimeQuery.setAttendPlanId(punchCommonDTO.getAttendPlanId());
                    List<AttendPunchTimeDO> attendPunchTimeList = attendPunchTimeService.selectDO(attendPunchTimeQuery, tokenUser.getSiteId());
                    if (CollectionUtils.isEmpty(attendPunchTimeList)) {
                        throw new ParamException("当前考勤规则没有设置时间段!");
                    }
                    attendRulePunchTime = attendPunchTimeList.get(0).to(AttendRulePunchTimeAddDTO.class);
                    attendRulePunchTime.setWorkWeekList(workWeekList);
                    attendRulePunchTime.setPeriodList(JSON.parseArray(attendPunchTimeList.get(0).getPeriodJson(), AttendPeriodDTO.class));
                    attendRulePunchTime.setAttendPunchTimeId(attendPunchTimeList.get(0).getId());
                    periodList = attendRulePunchTime.getPeriodList();
                } else {
                    isTodayAttend = false;
                    log.info("同步中国节假日---> 不需要打卡");
                    return new PunchCommonVO().buildNotNeedAttend();
                }
            }
        }

        // 如果考勤规则是自由上下班
        if (punchCommonDTO.getPlanType().equals(RuleType.FREE_WORK.getValue()) && !isSpecialPunch) {
            log.info("勤规则类型- 自由上下班");
            WorkStatusCommonVO workStatusCommonVO = this.freeCommute(punchCommonDTO, tokenUser.getSiteId());
            punchCommonVO = workStatusCommonVO.to(PunchCommonVO.class);
            // 今日是否需要考勤;true-是,false-否
            punchCommonVO.setIsTodayAttend(isTodayAttend);
            // 是否白名单;true-是,false-否
            punchCommonVO.setIsWhitelist(isWhitelist);
            punchCommonVO.setIsSpecialDate(attendNeedPunchVO.getIsSpecialDate());
            // 特殊日期是否需要打卡;true-是,false-否
            punchCommonVO.setIsNeedPunchOfDaily(attendNeedPunchVO.getIsNeedPunchOfDaily());
            // 考勤时段
            punchCommonVO.setPeriodList(periodList);
            // 考勤周
            punchCommonVO.setWorkWeekList(workWeekList);
            // 考勤高级设置
            punchCommonVO.setAdvanced(advanced);
            punchCommonVO.setPeriod("00:00-00:00");

            return punchCommonVO;
        }

        // 是特殊日期，特殊日期需要打卡
        if (isSpecialPunch) {
            periodList = JSON.parseArray(attendNeedPunchVO.getAttendPeriodJson(), AttendPeriodDTO.class);
            firstOnWork = periodList.get(0).getStartPeriod();
            lastOffWork = periodList.get(periodList.size() - 1).getEndPeriod();
            // 特殊日期使用固定上下班规则计算打卡时间相关
            attendRulePunchTime = attendPunchTimeService.puncheTimeForSpecial(punchCommonDTO.getAttendPlanId(), punchCommonDTO.getAttendDateStr(), tokenUser);
        }


        // 考勤时段排序
        if (CollectionUtils.isEmpty(periodList)) {
            return new PunchCommonVO().buildNotNeedAttend();
//            throw new HrException("考勤时段为空");
        }
        // TODO 待前端页面传参 sort
        if (!CollectionUtils.isEmpty(periodList)) {
            periodList.stream().forEach(item -> item.setSort(1));
        }
        periodList.sort(Comparator.comparingInt(AttendPeriodDTO::getSort));

        // 将考勤时段转换成时间点
        List<AttendPointTimeVO> pointTimeList = new ArrayList<>();
        //  上班开始时间
        String startOnWorkTime = periodList.get(0).getStartPeriod();
        // 最早上班打卡时间
        Date earliestPunchTime = null;
        if (advanced != null) {
            try {
                earliestPunchTime = org.jsola.hr.common.DateUtils.addMinutes(punchCommonDTO.getAttendDateStr() + " " + startOnWorkTime
                        , -Integer.valueOf(advanced.getEarliestPunchTime()).intValue());
            } catch (Exception e) {
                e.printStackTrace();
            }
            AttendPointTimeVO earliestPunchTimePoint = new AttendPointTimeVO();
            earliestPunchTimePoint.setPointTime(DateFormatUtils.format(earliestPunchTime, "HH:mm"));
            earliestPunchTimePoint.setType(0);
            pointTimeList.add(earliestPunchTimePoint);
            log.info("最早上班打卡时间:" + earliestPunchTimePoint);
        }

        String period = "";

        for (AttendPeriodDTO attendPeriod : periodList) {
            // 工作时段
            if (attendPeriod.getType() == 1) {

                AttendPointTimeVO onWorkPoint = new AttendPointTimeVO();
                onWorkPoint.setPointTime(attendPeriod.getStartPeriod());
                onWorkPoint.setType(1);
                onWorkPoint.setSort(attendPeriod.getSort());
                pointTimeList.add(onWorkPoint);

                AttendPointTimeVO offWorkPoint = new AttendPointTimeVO();
                offWorkPoint.setPointTime(attendPeriod.getEndPeriod());
                offWorkPoint.setType(2);
                offWorkPoint.setSort(attendPeriod.getSort());
                pointTimeList.add(offWorkPoint);

                if (StringUtils.isBlank(period)) {
                    period = attendPeriod.getStartPeriod() + "-" + attendPeriod.getEndPeriod();
                } else {
                    period = period + ";" + attendPeriod.getStartPeriod() + "-" + attendPeriod.getEndPeriod();
                }
            }
        }

        String endOffWorkTime = periodList.get(periodList.size() - 1).getEndPeriod();

        // 下班结束时间
        // 打卡时间限制 xxx 可打上班的卡,xxx可打下班的卡
        // 最晚下班打卡时间
        Date latestPunchTime = null;
        if (advanced != null) {
            try {
                latestPunchTime = org.jsola.hr.common.DateUtils.addMinutes(punchCommonDTO.getAttendDateStr() + " " + endOffWorkTime
                        , Integer.valueOf(advanced.getLatestPunchTime()).intValue());
            } catch (Exception e) {
                e.printStackTrace();
            }
            AttendPointTimeVO latestPunchTimePoint = new AttendPointTimeVO();
            latestPunchTimePoint.setPointTime(DateFormatUtils.format(latestPunchTime, "HH:mm"));
            latestPunchTimePoint.setType(3);
            pointTimeList.add(latestPunchTimePoint);
            log.info("最晚下班打卡时间：" + latestPunchTimePoint);
        }

        // 考勤时间点
        if (pointTimeList.size() > 0) {
            pointTimeList.forEach(pointTime -> {
                log.info("时间点:" + pointTime.getPointTime() + " 时间点类型:" + PointTimeType.find(pointTime.getType()).getDesc());
            });
        }

        // 按照考勤时间段打卡
        WorkStatusCommonVO workStatusCommonVO = null;

        // 将最早打卡时间和最晚打卡时间添加到考勤时间点中
        if (pointTimeList.size() > 0) {
            index:
            for (int i = 0; i < pointTimeList.size(); i++) {

                // 当前的
                AttendPointTimeVO startPoint = pointTimeList.get(i);
                // 下一个
                AttendPointTimeVO endPoint = pointTimeList.get(i + 1);
                try {
                    // 多个时间段打卡
                    for (AttendPeriodDTO attendPeriod : periodList) {
                        workStatusCommonVO = this.periodPunch(startPoint, endPoint, punchCommonDTO, attendRulePunchTime, attendPeriod.getStartPeriod(), attendPeriod.getEndPeriod(), tokenUser);
                        if (workStatusCommonVO.getIsInPunchTime() && workStatusCommonVO.getCompletedWork()) {
                            continue;
                        }
                        if (workStatusCommonVO.getIsInPunchTime() && !workStatusCommonVO.getCompletedWork()) {
                            break index;
                        }
                    }
                } catch (ParseException e) {
                    e.printStackTrace();
                }

                if (i == pointTimeList.size() - 2) {
                    break;
                }
            }
        }


        if (workStatusCommonVO != null) {
            punchCommonVO = workStatusCommonVO.to(PunchCommonVO.class);
        }

        // 允许迟到早退
        if (advanced != null && advanced.getIsAllowedLateEarly() == 1) {
            log.info("允许迟到早退");
            // 上班还是下班;0-缺卡1-上班打卡,2-下班打卡,3-今日已完成打卡 如果是上班
            if (punchCommonVO.getWorkStatus() == 1) {
                // 上班是否迟到
                if (punchCommonVO.getPunchStatus() == 2) {
                    // 上班允许迟到时间大于等于迟到时间，则上班为正常状态
                    if (advanced.getLateMinute() >= punchCommonVO.getMinutes()) {
                        punchCommonVO.setPunchStatus(1);
                        punchCommonVO.setMinutes(0);
                    }
                    // 如果上班允许迟到时间小于迟到时间，则计算实际的迟到时间
                    else if (advanced.getLateMinute() < punchCommonVO.getMinutes()) {
                        punchCommonVO.setMinutes(punchCommonVO.getMinutes() - advanced.getLateMinute());
                    }
                }
            }
            // 如果是下班打卡
            else if (punchCommonVO.getWorkStatus() == 2) {
                // 下班是否早退
                if (punchCommonVO.getPunchStatus() == 3) {
                    // 下班允许早退时间大于等于早退时间，则下班为正常状态
                    if (advanced.getEarlyMinute() >= punchCommonVO.getMinutes()) {
                        punchCommonVO.setPunchStatus(1);
                        punchCommonVO.setMinutes(0);
                    }
                    // 下班允许早退时间小于早退时间，计算早退了多长时间
                    else if (advanced.getEarlyMinute() < punchCommonVO.getMinutes()) {
                        punchCommonVO.setMinutes(punchCommonVO.getMinutes() - advanced.getEarlyMinute());
                    }
                }
            }
        }

        // 允许晚走晚到;1-允许,0-不允许 前提条件：不允许迟到早退
        if (advanced != null && advanced.getIsAllowLateLate() == 1 && advanced.getIsAllowedLateEarly() == 0) {
            log.info("允许晚走晚到");
            // 如果上班迟到
            if (punchCommonVO.getPunchStatus() == 2) {
                // 上班晚到 大于等于 迟到分钟数
                if (advanced.getOnWorkLateMinute() >= punchCommonVO.getMinutes()) {
                    punchCommonVO.setPunchStatus(1);
                    punchCommonVO.setMinutes(0);
                } else if (advanced.getOnWorkLateMinute() < punchCommonVO.getMinutes()) {
                    punchCommonVO.setMinutes(punchCommonVO.getMinutes() - advanced.getOnWorkLateMinute());
                }
            }
        }

        // 今日是否需要考勤;true-是,false-否
        punchCommonVO.setIsTodayAttend(isTodayAttend);
        // 是否白名单;true-是,false-否
        punchCommonVO.setIsWhitelist(isWhitelist);
        // 是否特殊日期;true-是,false-否
        punchCommonVO.setIsSpecialDate(attendNeedPunchVO.getIsSpecialDate());
        // 特殊日期是否需要打卡;true-是,false-否
        punchCommonVO.setIsNeedPunchOfDaily(attendNeedPunchVO.getIsNeedPunchOfDaily());
        // 考勤时段
        punchCommonVO.setPeriodList(periodList);
        // 考勤周
        punchCommonVO.setWorkWeekList(workWeekList);
        // 考勤高级设置
        punchCommonVO.setAdvanced(advanced);

        punchCommonVO.setPeriod(period);

        return punchCommonVO;
    }

    /**
     * 自由上下班
     *
     * @param punchCommonDTO
     * @param siteId
     * @return
     */
    private WorkStatusCommonVO freeCommute(PunchCommonDTO punchCommonDTO, String siteId) {

        // 查询当前的考勤打卡时间是否有上的卡
        AttendPunchRecordDO onWorkRecord = attendPunchRecordService.selectOnWorkRecord(punchCommonDTO.getAttendPlanId(), 0, punchCommonDTO.getEmpInfoId(), punchCommonDTO.getAttendDateStr(), siteId);
        AttendPunchRecordDO offWorkRecord = attendPunchRecordService.selectOffWorkRecord(punchCommonDTO.getAttendPlanId(), 0, punchCommonDTO.getEmpInfoId(), punchCommonDTO.getAttendDateStr(), siteId);

        // 上班时间(HH:mm)
        String onWorkTime = "";
        // 下班时间(HH:mm)
        String offWorkTime = "";
        // 上班还是下班;0-异常打卡1-上班打卡,2-下班打卡,3-今日已完成打卡
        int workStatus = 0;
        // 上班打卡时间
        String onWorkPunchTime = "";
        // 下班打卡时间
        String offWorkPunchTime = "";
        // 打卡状态 ; 0-缺卡,1-正常,2-迟到,3-早退
        int punchStatus = 1;
        // 迟到或早退分钟数
        int minutes = 0;
        // 打卡序号
        String punchSerial = "";

        // 上班打卡
        if (onWorkRecord == null && offWorkRecord == null) {
            onWorkTime = punchCommonDTO.getPunchTimeStr();
            onWorkPunchTime = punchCommonDTO.getPunchTimeStr();
            workStatus = 1;
            punchSerial = "0-1";
        }
        // 下班打卡
        else if (onWorkRecord != null && offWorkRecord == null) {
            offWorkTime = punchCommonDTO.getPunchTimeStr();
            offWorkPunchTime = punchCommonDTO.getPunchTimeStr();
            workStatus = 2;
            punchSerial = "0-2";
        }
        // 上班打卡
        else if (onWorkRecord != null && offWorkRecord != null) {
            onWorkTime = punchCommonDTO.getPunchTimeStr();
            onWorkPunchTime = punchCommonDTO.getPunchTimeStr();
            workStatus = 1;
            punchSerial = "0-1";
        }

        WorkStatusCommonVO workStatusCommonVO = new WorkStatusCommonVO();

        workStatusCommonVO.setCompletedWork(false);
        workStatusCommonVO.setOnWorkTime(onWorkTime);
        workStatusCommonVO.setOffWorkTime(offWorkTime);
        workStatusCommonVO.setWorkStatus(workStatus);
        workStatusCommonVO.setOnWorkPunchTime(onWorkPunchTime);
        workStatusCommonVO.setOffWorkPunchTime(offWorkPunchTime);
        workStatusCommonVO.setPunchStatus(punchStatus);
        workStatusCommonVO.setMinutes(minutes);

        return workStatusCommonVO;
    }

    /**
     * 按照考勤时间段打卡
     *
     * @param startPoint
     * @param endPoint
     * @param punchCommonDTO
     * @param tokenUser
     * @return
     * @throws ParseException
     */
    private WorkStatusCommonVO periodPunch(AttendPointTimeVO startPoint, AttendPointTimeVO endPoint, PunchCommonDTO punchCommonDTO, AttendRulePunchTimeAddDTO attendRulePunchTime, String firstOnWork, String lastOffWork, TokenUser tokenUser) throws ParseException {
        log.info("按照考勤时间段打卡：开始时间点:" + startPoint.getPointTime() + " 结束时间点:" + endPoint.getPointTime() + " 打卡时间点:" + punchCommonDTO.getPunchTimeStr());
        // 上班时间(HH:mm)
        String onWorkTime = "";
        // 下班时间(HH:mm)
        String offWorkTime = "";
        // 上班还是下班;0-异常打卡1-上班打卡,2-下班打卡,3-今日已完成打卡,4-未到打卡时间
        int workStatus = 0;
        // 上班打卡或下班打卡是否完成
        boolean completedWork = false;
        // 上班打卡时间
        Date now = DateKit.getToday();
        // 下班打卡时间
        String offWorkPunchTime = "";
        // 打卡状态 ; 0-缺卡,1-正常,2-迟到,3-早退
        int punchStatus = 1;
        // 迟到或早退分钟数
        int minutes = 0;
        // 是否匹配到
        boolean isMatch = false;
        // 打卡序号
        String punchSerial = "";

        // 打卡时间 (yyyy-MM-dd HH:mm)
        String punchTimeStr = punchCommonDTO.getAttendDateStr() + " " + punchCommonDTO.getPunchTimeStr();
        // 打卡时间转为Date
        Date punchTimeDate = DateUtils.parseDate(punchTimeStr, "yyyy-MM-dd HH:mm");
        // Date转为 毫秒数
        long punchTimeMillis = punchTimeDate.getTime();

        int punchInt = Integer.parseInt(punchCommonDTO.getPunchTimeStr().replace(":", ""));
        int startInt = Integer.parseInt(startPoint.getPointTime().replace(":", ""));
        int endInt = Integer.parseInt(endPoint.getPointTime().replace(":", ""));

        // 开始的时间点
        String startTimeStr = "";
        Date startDate = null;
        long startMillis = 0;

        // 这个时间段,结束的时间点
        String endTimeStr = punchCommonDTO.getAttendDateStr() + " " + endPoint.getPointTime();
        // 时间点转Date
        Date endDate = DateUtils.parseDate(endTimeStr, "yyyy-MM-dd HH:mm");
        // Date转毫秒数
        long endMillis = endDate.getTime();

        // 开始的时间点取前一天
        // 开始时间和打卡时间在同一天，结束时间在次日
        if (startInt <= 235959 && punchInt <= 235959 && startInt > endInt) {
            log.info("开始时间和打卡时间在同一天，结束时间在次日");
            startTimeStr = punchCommonDTO.getAttendDateStr() + " " + startPoint.getPointTime();
            startDate = DateUtils.parseDate(startTimeStr, "yyyy-MM-dd HH:mm");
            startMillis = startDate.getTime();

            // 结束时间在次日
            String beforeDay = org.jsola.hr.common.DateUtils.getBeforeDateByDate(punchCommonDTO.getAttendDateStr());
            endTimeStr = beforeDay + " " + endPoint.getPointTime();
            endDate = DateUtils.parseDate(endTimeStr, "yyyy-MM-dd HH:mm");
            endMillis = endDate.getTime();
        }
        // 打卡时间和结束时间在同一天，开始时间在昨日
        else if (startInt > punchInt && startInt > endInt) {
            log.info("打卡时间和结束时间在同一天，开始时间在昨日");
            String preDay = org.jsola.hr.common.DateUtils.getPreDateByDate(punchCommonDTO.getAttendDateStr());
            startTimeStr = preDay + " " + startPoint.getPointTime();
            startDate = DateUtils.parseDate(startTimeStr, "yyyy-MM-dd HH:mm");
            startMillis = startDate.getTime();
        }
        // 开始时间、打卡时间、结束时间都在同一天
        else if (startInt <= 235959 && punchInt <= 235959 && endInt <= 235959) {
            log.info("开始时间、打卡时间、结束时间都在同一天");
            startTimeStr = punchCommonDTO.getAttendDateStr() + " " + startPoint.getPointTime();
            startDate = DateUtils.parseDate(startTimeStr, "yyyy-MM-dd HH:mm");
            startMillis = startDate.getTime();
        }

        WorkStatusCommonVO workStatusCommonVO = new WorkStatusCommonVO();
        Date earliestPunchDate = null;
        Date latestOnPunchDate = null;
        Date earliestOffPunchDate = null;
        Date latestPunchDate = null;
        String nowStr = DateKit.getDateStr(now, "yyyy-MM-dd");
        try {
            // 最早上班打卡时间
            earliestPunchDate = org.jsola.hr.common.DateUtils.addMinutes(nowStr + " " + firstOnWork, (-1) * Integer.valueOf(attendRulePunchTime.getEarliestPunchTime()));
            // 最晚上班打卡时间
            latestOnPunchDate = org.jsola.hr.common.DateUtils.addMinutes(nowStr + " " + firstOnWork, Integer.valueOf(attendRulePunchTime.getLatestOnPunchTime()));
            // 最早下班打卡时间
            earliestOffPunchDate = org.jsola.hr.common.DateUtils.addMinutes(nowStr + " " + lastOffWork, (-1) * Integer.valueOf(attendRulePunchTime.getEarliestOffPunchTime()));
            // 最晚下班打卡时间
            latestPunchDate = org.jsola.hr.common.DateUtils.addMinutes(nowStr + " " + lastOffWork, Integer.valueOf(attendRulePunchTime.getLatestPunchTime()));
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 最早上班打卡时间(分钟)
        String earliestPunchTime = attendRulePunchTime.getEarliestPunchTime();
        // 最晚上班打卡时间(分钟)
        String latestOnPunchTime = attendRulePunchTime.getLatestOnPunchTime();
        // 最早下班打卡时间(分钟)
        String earliestOffPunchTime = attendRulePunchTime.getEarliestOffPunchTime();
        // 最晚下班打卡时间(分钟)
        String latestPunchTime = attendRulePunchTime.getLatestPunchTime();
        // 上班打卡标准时间 - 当前时间
        Integer onWorkBeforeDiff = org.jsola.hr.common.DateUtils.getMinutesDiff(DateKit.getDate("yyyy-MM-dd HH:mm", nowStr + " " + firstOnWork), now);
        // 当前时间 - 上班打卡标准时间
        Integer onWorkAfterDiff = org.jsola.hr.common.DateUtils.getMinutesDiff(now, DateKit.getDate("yyyy-MM-dd HH:mm", nowStr + " " + firstOnWork));
        // 下班打卡标准时间 - 当前时间
        Integer offWorkBeforeDiff = org.jsola.hr.common.DateUtils.getMinutesDiff(DateKit.getDate("yyyy-MM-dd HH:mm", nowStr + " " + lastOffWork), now);
        // 当前时间 - 下班打卡标准时间
        Integer offWorkAfterDiff = org.jsola.hr.common.DateUtils.getMinutesDiff(now, DateKit.getDate("yyyy-MM-dd HH:mm", nowStr + " " + lastOffWork));
        // 当前时间是否在上下班打卡时间段
        if (onWorkBeforeDiff < Integer.parseInt(earliestPunchTime) &&
                onWorkAfterDiff < Integer.parseInt(latestOnPunchTime)) {
            // 上班卡
            log.info("匹配到了打卡时间在某一个时间段范围内: 打卡时间:" + punchCommonDTO.getPunchTimeStr() + " 时间段:" + earliestPunchDate + " - " + latestOnPunchDate);
            // 上班打卡时间范围内
            workStatusCommonVO.setWorkStatus(1);
            workStatusCommonVO.setIsInPunchTime(true);
            // 查询这段时间的打卡记录
            List<AttendPunchRecordDO> attendPunchRecordDOList = isPunchInTimeCheck(punchCommonDTO, earliestPunchDate, latestOnPunchDate, firstOnWork, lastOffWork, PunchType.ON_WORK.getValue(), tokenUser);
            if (CollectionUtils.isEmpty(attendPunchRecordDOList)) {
                // 该时间段内未打过卡
                workStatusCommonVO.setCompletedWork(false);
                workStatusCommonVO.setOnWorkPunchTime(punchCommonDTO.getPunchTimeStr());
                // 是否迟到
                if (onWorkAfterDiff > 0 && onWorkAfterDiff > Integer.parseInt(latestOnPunchTime)) {
                    // 迟到打卡
                    workStatusCommonVO.setPunchStatus(2);
                    workStatusCommonVO.setMinutes(onWorkAfterDiff);
                } else {
                    // 正常打卡
                    workStatusCommonVO.setPunchStatus(1);
                    workStatusCommonVO.setMinutes(0);
                }
            } else {
                // 该时间段内已打过卡
                workStatusCommonVO.setCompletedWork(true);
                // 下班卡（如果设置打卡时间范围重叠）
                if (offWorkBeforeDiff < Integer.valueOf(earliestOffPunchTime) && offWorkAfterDiff < Integer.valueOf(latestPunchTime)) {
                    punchOffWork(punchCommonDTO,
                            earliestOffPunchDate,
                            latestPunchDate,
                            earliestPunchDate,
                            latestOnPunchDate,
                            offWorkBeforeDiff, Integer.parseInt(earliestOffPunchTime),
                            workStatusCommonVO,
                            firstOnWork,
                            lastOffWork,
                            now,
                            tokenUser);
                } else if (offWorkBeforeDiff > 0) {
                    workStatusCommonVO.setCompletedWork(false);
                    workStatusCommonVO.setWorkStatus(2);
                    workStatusCommonVO.setOffWorkPunchTime(punchCommonDTO.getPunchTimeStr());
                    workStatusCommonVO.setPunchStatus(3);
                    workStatusCommonVO.setMinutes(offWorkBeforeDiff);
                    workStatusCommonVO.setIsInPunchTime(true);
                }
            }

        } else if (offWorkBeforeDiff < Integer.valueOf(earliestOffPunchTime) &&
                offWorkAfterDiff < Integer.valueOf(latestPunchTime)) {
            // 先判断上班卡打过没,没有打则过打迟到卡
            // 在下班时间内,但是还未到最早上班时间
            List<AttendPunchRecordDO> attendPunchRecordDOListOn = isPunchInTimeCheck(punchCommonDTO, earliestPunchDate, latestOnPunchDate, firstOnWork, lastOffWork, PunchType.ON_WORK.getValue(), tokenUser);
            if (onWorkAfterDiff < 0 && Math.abs(onWorkAfterDiff) >= Integer.parseInt(earliestPunchTime)) {
                // 不在打卡时间范围内
                workStatusCommonVO.setIsInPunchTime(false);
                workStatusCommonVO.setWorkStatus(0);
                workStatusCommonVO.setCompletedWork(false);
            } else if (CollectionUtils.isEmpty(attendPunchRecordDOListOn)) {
                // 上班时间未打卡
                workStatusCommonVO.setIsInPunchTime(true);
                workStatusCommonVO.setCompletedWork(false);
                workStatusCommonVO.setWorkStatus(1);
                workStatusCommonVO.setOnWorkPunchTime(punchCommonDTO.getPunchTimeStr());
                workStatusCommonVO.setPunchStatus(2);
                workStatusCommonVO.setMinutes(onWorkAfterDiff);
            } else {
                // 下班卡
                punchOffWork(punchCommonDTO, earliestOffPunchDate, latestPunchDate, earliestPunchDate, latestOnPunchDate, offWorkBeforeDiff, Integer.parseInt(earliestOffPunchTime), workStatusCommonVO, firstOnWork, lastOffWork, now, tokenUser);
            }
        } else {
            // 不在打卡时间范围内
            workStatusCommonVO.setIsInPunchTime(false);
            // 查询这段时间的打卡记录
            List<AttendPunchRecordDO> attendPunchRecordListOn = isPunchInTimeCheck(punchCommonDTO, earliestPunchDate, latestOnPunchDate, firstOnWork, lastOffWork, PunchType.ON_WORK.getValue(), tokenUser);
            List<AttendPunchRecordDO> attendPunchRecordListOff = isPunchInTimeCheck(punchCommonDTO, earliestOffPunchDate, latestPunchDate, firstOnWork, lastOffWork, PunchType.OFF_WORK.getValue(), tokenUser);
            log.info("不在打卡时间范围内: 是否有上班记录" + CollectionUtils.isEmpty(attendPunchRecordListOn));
            log.info("不在打卡时间范围内: 是否有下班记录" + CollectionUtils.isEmpty(attendPunchRecordListOff));
            if (!CollectionUtils.isEmpty(attendPunchRecordListOn) &&
                    !CollectionUtils.isEmpty(attendPunchRecordListOff)) {
                // 时间段内已完成打卡
                workStatusCommonVO.setWorkStatus(3);
                workStatusCommonVO.setCompletedWork(true);
            } else {
                // 时间段内未完成打卡
                workStatusCommonVO.setWorkStatus(0);
                workStatusCommonVO.setCompletedWork(false);
            }
            // 超过了最晚上班时间,并且还没打上班卡, 打迟到卡
            if (CollectionUtils.isEmpty(attendPunchRecordListOn)) {
                if (onWorkAfterDiff > 0 && onWorkAfterDiff > Integer.parseInt(latestOnPunchTime)) {
                    workStatusCommonVO.setCompletedWork(false);
                    workStatusCommonVO.setWorkStatus(1);
                    workStatusCommonVO.setOnWorkPunchTime(punchCommonDTO.getPunchTimeStr());
                    workStatusCommonVO.setPunchStatus(2);
                    workStatusCommonVO.setMinutes(onWorkAfterDiff);
                    workStatusCommonVO.setIsInPunchTime(true);
                }
            } else if (CollectionUtils.isEmpty(attendPunchRecordListOff)) {
                // 当前时间未到最早下班时间, 并且下班未打卡, 打早退卡
                if (offWorkBeforeDiff > 0) {
                    workStatusCommonVO.setCompletedWork(false);
                    workStatusCommonVO.setWorkStatus(2);
                    workStatusCommonVO.setOffWorkPunchTime(punchCommonDTO.getPunchTimeStr());
                    workStatusCommonVO.setPunchStatus(3);
                    workStatusCommonVO.setMinutes(offWorkBeforeDiff);
                    workStatusCommonVO.setIsInPunchTime(true);
                } else if (offWorkAfterDiff > 0 && offWorkAfterDiff > Integer.parseInt(latestPunchTime)) {
                    workStatusCommonVO.setIsInPunchTime(true);
                    workStatusCommonVO.setWorkStatus(2);
                    workStatusCommonVO.setOffWorkPunchTime(punchCommonDTO.getPunchTimeStr());
                    workStatusCommonVO.setCompletedWork(false);
                    workStatusCommonVO.setPunchStatus(1);
                    workStatusCommonVO.setMinutes(0);
                }
            } else {
                if (offWorkBeforeDiff > 0) {
                    workStatusCommonVO.setCompletedWork(false);
                    workStatusCommonVO.setWorkStatus(2);
                    workStatusCommonVO.setOffWorkPunchTime(punchCommonDTO.getPunchTimeStr());
                    workStatusCommonVO.setPunchStatus(3);
                    workStatusCommonVO.setMinutes(offWorkBeforeDiff);
                    workStatusCommonVO.setIsInPunchTime(true);
                }
            }
            log.info("不在打卡时间范围内: " + workStatusCommonVO);
        }

        workStatusCommonVO.setOnWorkTime(nowStr + " " + firstOnWork);
        workStatusCommonVO.setOffWorkTime(nowStr + " " + lastOffWork);
//        workStatusCommonVO.setOnWorkTime(onWorkTime);
//        workStatusCommonVO.setOffWorkTime(offWorkTime);
//        workStatusCommonVO.setWorkStatus(workStatus);
//        workStatusCommonVO.setCompletedWork(completedWork);
//        workStatusCommonVO.setOnWorkPunchTime(onWorkPunchTime);
//        workStatusCommonVO.setOffWorkPunchTime(offWorkPunchTime);
//        workStatusCommonVO.setPunchStatus(punchStatus);
//        workStatusCommonVO.setMinutes(minutes);
        workStatusCommonVO.setIsMatch(isMatch);
        workStatusCommonVO.setPunchSerial(punchSerial);

        return workStatusCommonVO;
    }

    public List<AttendPunchRecordDO> isPunchInTimeCheck(PunchCommonDTO punchCommonDTO, Date earliestPunchDate, Date latestOnPunchDate, String firstOnWork, String lastOffWork, Integer punchType, TokenUser tokenUser) {
        AttendPunchRecordQuery attendPunchRecordQuery = new AttendPunchRecordQuery();

        attendPunchRecordQuery.setAttendPlanId(punchCommonDTO.getAttendPlanId());
        attendPunchRecordQuery.setEmpInfoId(punchCommonDTO.getEmpInfoId());

        List<String> standardTimeLst = new ArrayList<>();
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String onWorkStandardTime = null;
        String offWorkStandardTime = null;
        try {
            onWorkStandardTime = df.format(df.parse(punchCommonDTO.getAttendDateStr() + " " + firstOnWork + ":00"));
            offWorkStandardTime = df.format(df.parse(punchCommonDTO.getAttendDateStr() + " " + lastOffWork + ":00"));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (PunchType.ON_WORK.getValue().equals(punchType)) {
            // 上班打卡
            standardTimeLst.add(onWorkStandardTime);
        } else if (PunchType.OFF_WORK.getValue().equals(punchType)) {
            // 下班打卡
            standardTimeLst.add(offWorkStandardTime);
        }
        attendPunchRecordQuery.setStandardTimeLst(standardTimeLst);

        List<AttendPunchRecordDO> dataList = attendPunchRecordService.selectDO(attendPunchRecordQuery, tokenUser.getSiteId());
//        if(CollectionUtils.isEmpty(dataList)){
//            // 该时间段内未打过卡
//            return false;
//        }else{
//            // 该时间段内已打过卡
//            return true;
//        }
        return dataList;
    }

    public void punchOffWork(PunchCommonDTO punchCommonDTO,
                             Date earliestOffPunchDate,
                             Date latestPunchDate,
                             Date earliestPunchDate,
                             Date latestOnPunchDate,
                             Integer offWorkBeforeDiff,
                             Integer earliestOffPunchTime,
                             WorkStatusCommonVO workStatusCommonVO,
                             String firstOnWork,
                             String lastOffWork,
                             Date now,
                             TokenUser tokenUser) {
        log.info("匹配到了打卡时间在某一个时间段范围内: 打卡时间:" + punchCommonDTO.getPunchTimeStr() + " 时间段:" + earliestOffPunchDate + " - " + latestPunchDate);
        // 下班打卡时间范围内
        workStatusCommonVO.setIsInPunchTime(true);
        workStatusCommonVO.setWorkStatus(2);
        // 查询这段时间的打卡记录
        List<AttendPunchRecordDO> attendPunchRecordDOList = isPunchInTimeCheck(punchCommonDTO, earliestOffPunchDate, latestPunchDate, firstOnWork, lastOffWork, PunchType.OFF_WORK.getValue(), tokenUser);
        // 查询上班时间的打卡记录
        List<AttendPunchRecordDO> attendPunchRecordDOListOn = isPunchInTimeCheck(punchCommonDTO, earliestPunchDate, latestOnPunchDate, firstOnWork, lastOffWork, PunchType.ON_WORK.getValue(), tokenUser);
        if (CollectionUtils.isEmpty(attendPunchRecordDOList)) {
            // 该时间段内未打过卡
            workStatusCommonVO.setCompletedWork(false);
            workStatusCommonVO.setOffWorkPunchTime(punchCommonDTO.getPunchTimeStr());
            // 是否早退
            if (offWorkBeforeDiff > 0 ) {
                // 早退打卡
                workStatusCommonVO.setPunchStatus(3);
                workStatusCommonVO.setMinutes(offWorkBeforeDiff);
            } else {
                // 正常打卡
                workStatusCommonVO.setPunchStatus(1);
                workStatusCommonVO.setMinutes(0);
            }
        } else {
            // 该时间段内已打过卡
            workStatusCommonVO.setCompletedWork(true);
            workStatusCommonVO.setOffWorkPunchTime(punchCommonDTO.getPunchTimeStr());
            if (Objects.nonNull(attendPunchRecordDOList.get(0).getPunchTime())) {
                // 最后一次下班打卡时间
                workStatusCommonVO.setOffWorkPunchTimeLast(DateKit.getDateStr(attendPunchRecordDOList.get(0).getPunchTime(), "yyyy-MM-dd HH:mm:ss"));
            }
            // 是否早退
            if (offWorkBeforeDiff > 0) {
                // 早退打卡
                workStatusCommonVO.setPunchStatus(3);
                workStatusCommonVO.setMinutes(offWorkBeforeDiff);
            } else {
                // 正常打卡
                workStatusCommonVO.setPunchStatus(1);
                workStatusCommonVO.setMinutes(0);
            }
            // 上班是否打过卡
            if (!CollectionUtils.isEmpty(attendPunchRecordDOListOn)) {
                // 今日已完成打卡
                workStatusCommonVO.setWorkStatus(3);
            }
        }
        if (!CollectionUtils.isEmpty(attendPunchRecordDOListOn)) {
            // 上班打过卡，则计算今日工作时长
            if (Objects.nonNull(attendPunchRecordDOListOn.get(0).getPunchTime())) {
                Integer workTime = org.jsola.hr.common.DateUtils.getMinutesDiff(now, attendPunchRecordDOListOn.get(0).getPunchTime());
                Integer restTime = attendPunchCalibrationService.restTime(punchCommonDTO.getAttendPlanId(), tokenUser.getSiteId());
                workTime = (workTime - restTime) >= 0 ? (workTime - restTime) : 0;
                workStatusCommonVO.setWorkTime(workTime);
            }
        }
    }

}
